import  json
import dateutil
import dateutil.parser
import os
import shutil
import zipfile
from io import BytesIO

import requests


class Garmin():
    CONNECT_MODERN_ACTIVITIES = "https://connect.garmin.cn/modern/proxy/activitylist-service/activities/search/activities"
    # {id}
    CONNECT_MODERN_ACTIVITIES_DOWNLOAD_FILES = "https://connect.garmin.cn/modern/proxy/download-service/files/activity/{}"

class GarminUtil():
    cookies = 'none'
    def __init__(self, cookies):
        self.cookies = cookies

    def _fetch_activity_ids_and_ts(self, start_index, max_limit=20):
        """批量获取 active 的 ids 和 ts
        返回一个从给定索引开始的活动 id 序列(以及它们的起始时间戳) ，索引0是用户最近注册的活动。
        """
        headers = {
            "origin": "https://sso.garmin.cn",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"
        }

        response = requests.get(
            Garmin.CONNECT_MODERN_ACTIVITIES, params={"start": start_index, "limit": max_limit}, cookies = self.cookies
        )

        if response.status_code != 200:
            raise Exception(u"failed to fetch activities {} to {} types: {}\n{}"
                            .format(start_index, (start_index + max_limit - 1), response.status_code, response.text))
        activities = json.loads(response.text)
        if not activities:
            return []

        entries = []
        for activity in activities:
            id = int(activity["activityId"])
            startTime = str(activity["startTimeLocal"])
            # timestamp_local = dateutil.parser.parse(activity["startTimeLocal"])
            # timestamp_utc = dateutil.parser.parse(activity["startTimeGMT"])
            # 转换为时间戳
            # timestamp_utc = timestamp_utc.replace(tzinfo=dateutil.tz.tzutc())
            # entries.append((id, timestamp_utc))
            entries.append((startTime.replace(":","_")[:16], id))
        return entries

    def _get_original_activity(self, activity_id):
        """获取活动原始值
        佳明设备导入才有原始值(如手动输入), 原始值中有fit文件
        :param activity_id: Activitiy ID
        :type activity_id: int
        :returns: A tuple of the file type(e.g. 'fit', 'tcx', 'gpx') and its contents, or :obj:`(None, None) if no file is found.
        :rtype: (str, str)
        """
        response = requests.get(Garmin.CONNECT_MODERN_ACTIVITIES_DOWNLOAD_FILES.format(activity_id), cookies = self.cookies)

        if response.status_code in [404, 500]:
            return None,None
        if response.status_code != 200:
            raise Exception(u"failed to get original activity file for {}: {}\n{}".format(activity_id, response.status_code, response.text))
        zip_file = zipfile.ZipFile(BytesIO(response.content), mode='r')

        for path in zip_file.namelist():
            # file_name, ext_name
            fn, ext = os.path.splitext(path)
            if fn.startswith(str(activity_id)):
                return ext[1:], zip_file.open(path).read()
        return None, None

    def _get_activity_fit(self, activity_id):
        """获取活动 fit 文件
        :param activity_id:
        :return:
        """
        ext, original_file = self._get_original_activity(activity_id)
        return original_file if ext == 'fit' else None

    def _fit_save(self, activity_id, file_name = None):
        """
        保存 fit 到 ./fit/2020-10-10
        :return:
        """
        if file_name is None:
            file_name = activity_id
        f = self._get_activity_fit(activity_id)

        if not f is None:
            with open("./fit/{}.fit".format(file_name), 'wb') as fit_file:
                fit_file.write(f)
                print("Save OK")

    def down_fit(self, start, limit):
        # 判断: 存在 fit 文件夹, 删除
        if os.path.exists("./fit"):
            shutil.rmtree("./fit/", ignore_errors=True)
        # 创建: fit 目录
        os.mkdir("./fit")

        entrys = self._fetch_activity_ids_and_ts(start,limit)
        for entry in entrys:
            print(f"{entry[0] + str(entry[1])}")

            activity_id = entry[1];
            print("保存 {} ....".format(entry[1]))
            self._fit_save(activity_id, entry[0])

